// The simplest fragment shader ( use to check the frament processing's bottleneck) 
var simplestFragmentShader = "\
  void main(void){\
    gl_FragColor = vec4(1.0,0.0,0.0,1.0);\
  }\
";

//
// gluLookAt
//
function makeLookAt(ex, ey, ez,
                    cx, cy, cz,
                    ux, uy, uz,
                    result)
{
  var forward = gewRegVec3;
  var up      = gewRegVec4;
  var right   = gewRegVec5;
  
  gewRegVec0.set(ex, ey, ez); // eye
  gewRegVec1.set(cx, cy, cz); // center
  gewRegVec2.set(ux, uy, uz); // up
  var mag; 
  gewRegVec1.sub(gewRegVec0,forward); // z = gewRegVec3
  forward.normalize();
  
  forward.cross(gewRegVec2, right);
  right.normalize();
  
  right.cross(forward, up);
  up.normalize();
  
  if(!result)
    result = new gewMat4([ right.x, up.x, - forward.x, 0, right.y, up.y, -forward.y, 0, right.z, up.z, -forward.z, 0, 0,0,0,1]);
  else
    result.set([ right.x, up.x, - forward.x, 0, right.y, up.y, -forward.y, 0, right.z, up.z, -forward.z, 0, 0,0,0,1]);
  gewMat4.getTranslateXYZ(-ex,-ey,-ez,gewRegMat0);
  result.mul(gewRegMat0,result);
  return result;
}

//
// glOrtho
//
function makeOrtho(left, right,
                   bottom, top,
                   znear, zfar,
                   result)
{
  var tx = -(right+left)/(right-left);
  var ty = -(top+bottom)/(top-bottom);
  var tz = -(zfar+znear)/(zfar-znear);
  if(!result)
    return new gewMat4([ 2/(right-left),0,0,0, 0,2/(top-bottom),0,0, 0,0, -2/(zfar-znear),0, tx,ty,tz,1]);
  else
  {
    result.set([ 2/(right-left),0,0,0, 0,2/(top-bottom),0,0, 0,0, -2/(zfar-znear),0, tx,ty,tz,1])
    return result;
  }
}

//
// gluPerspective
//
function makePerspective(fovy, aspect, znear, zfar,result)
{
  var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
  var ymin = -ymax;
  var xmin = ymin * aspect;
  var xmax = ymax * aspect;
  return makeFrustum(xmin, xmax, ymin, ymax, znear, zfar,result);
}

//
// glFrustum
//
function makeFrustum(left, right,
                      bottom, top,
                      znear, zfar,result)
{
  var X = 2*znear/(right-left);
  var Y = 2*znear/(top-bottom);
  var A = (right+left)/(right-left);
  var B = (top+bottom)/(top-bottom);
  var C = -(zfar+znear)/(zfar-znear);
  var D = -2*zfar*znear/(zfar-znear);
  if(!result)
    return new gewMat4([X,0,0,0, 0,Y,0,0, A,B,C,-1, 0,0,D,0]);
  else
  {
    result.set([X,0,0,0, 0,Y,0,0, A,B,C,-1, 0,0,D,0]);
    return result;
  }
}

function createGLSLShader(gl,shader,vsStr,fsStr)
{
  // Create program
  shader.vs = createVertexShader(gl, vsStr);
  shader.fs = createFragmentShader(gl,fsStr);  
  shader.program = gl.createProgram();
  gl.attachShader(shader.program, shader.vs);
  gl.attachShader(shader.program, shader.fs);
  gl.linkProgram(shader.program);
  checkProgramLinkStatus(gl, shader.program);
  
  // Bind atrribute& uniform
  var lines0 = vsStr.split('\n');
  var lines = [];
  for(var i=0;i<lines0.length;i++)
  {
    var temp = lines0[i].split(';');
    for(var l=0;l<temp.length;l++)
      lines.push(temp[l]);
  }
  var lineNum = lines.length;
  for(var i=0;i<lineNum;i++)
  {
    var atrr = lines[i].split(' ',3);
    if(atrr[0]=="attribute")
    {
      var name = atrr[2].split('[',1);
      if(shader[name[0] ] == undefined || shader[name[0] ] == null )
        shader[name[0] ] = gl.getAttribLocation(shader.program, name[0]);
    }
    else if(atrr[0]=="uniform")
    {
      var name = atrr[2].split('[',1);
      if(shader[name[0] ] == undefined || shader[name[0] ] == null )
        shader[name[0] ] = gl.getUniformLocation(shader.program, name[0]);
    }
  }
  
  lines = fsStr.split(';');
  lineNum = lines.length;
  for(var i=0;i<lineNum;i++)
  {
    var atrr = lines[i].split(' ',3);
    if(atrr[0]=="attribute")
    {
      var name = atrr[2].split('[',1);
      if(shader[name[0] ] == undefined || shader[name[0] ] == null )
        shader[name[0] ] = gl.getAttribLocation(shader.program, name[0]);
    }
    else if(atrr[0]=="uniform")
    {
      var name = atrr[2].split('[',1);
      if(shader[name[0] ] == undefined || shader[name[0] ] == null )
        shader[name[0] ] = gl.getUniformLocation(shader.program, name[0]);
    }
  }
}

function createVertexShader(gl,str)
{
  var shader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(shader, str);
  gl.compileShader(shader);
 
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("createVertexShader:Error!\n"+gl.getShaderInfoLog(shader) + "\n" + str);
return null;
}
  return shader;
}
function createFragmentShader(gl,str)
{
  var shader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(shader, str);
  gl.compileShader(shader);  
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert("createFragmentShader:Error!\n"+gl.getShaderInfoLog(shader) + "\n" + str);
    return null;
  }
  return shader;
}
//
// Create shader from shader's source id
//
function checkProgramLinkStatus(gl,program)
{
  var status = gl.getProgramParameter(program,gl.LINK_STATUS);
  if(status!=gl.TRUE)
  { 
    // Current disable because not run correctly on FF
    //alert("checkProgramLinkStatus:Link error!\n"+gl.getProgramInfoLog(program));
  }
}

function getFrustumFromMat(viewProjMat,frustum)
{
  var m = viewProjMat.data;
  // Left clipping plane
  frustum.plane[0].a = m[3] + m[0];
  frustum.plane[0].b = m[7] + m[4];
  frustum.plane[0].c = m[11] + m[8];
  frustum.plane[0].d = m[15] + m[12];

  // Right clipping plane
  frustum.plane[1].a = m[3] - m[0];
  frustum.plane[1].b = m[7] - m[4];
  frustum.plane[1].c = m[11] - m[8];
  frustum.plane[1].d = m[15] - m[12];

  // Near clipping plane
  frustum.plane[2].a = m[3] + m[2];
  frustum.plane[2].b = m[7] + m[6];
  frustum.plane[2].c = m[11] + m[10];
  frustum.plane[2].d = m[15] + m[14];

  // Far clipping plane
  frustum.plane[3].a = m[3] - m[2];
  frustum.plane[3].b = m[7] - m[6];
  frustum.plane[3].c = m[11] - m[10];
  frustum.plane[3].d = m[15] - m[14];

  // Top clipping plane
  frustum.plane[4].a = m[3] - m[1];
  frustum.plane[4].b = m[7] - m[5];
  frustum.plane[4].c = m[11] - m[9];
  frustum.plane[4].d = m[15] - m[13];


  // Bottom clipping plane
  frustum.plane[5].a = m[3] + m[1];
  frustum.plane[5].b = m[7] + m[5];
  frustum.plane[5].c = m[11] + m[9];
  frustum.plane[5].d = m[15] + m[13];

  // Normalize all plane
  frustum.plane[0].normalize();
  frustum.plane[1].normalize();
  frustum.plane[2].normalize();
  frustum.plane[3].normalize();
  frustum.plane[4].normalize();
  frustum.plane[5].normalize();
  // Calculate bitField for each plane
  for (var i = 0; i < 6; i++)
  {
      frustum.bitField[i] = 0;
      if (frustum.plane[i].a > 0)
          frustum.bitField[i] |= 0x1 << 2;
      if (frustum.plane[i].b > 0)
          frustum.bitField[i] |= 0x1 << 1;
      if (frustum.plane[i].c > 0)
          frustum.bitField[i] |= 0x1;
  }
}
// Get picking ray (from view point-> a canvas point) in world coordinate
function getPickingRay(canvas_x,canvas_y,camera,invertViewMat)
{
  var view_w = camera.m_viewport_size.x;
  var view_h = camera.m_viewport_size.y;
  
  var window_y = (view_h - canvas_y) - view_h/2;
  var norm_y = window_y/(view_h/2);
  var window_x = canvas_x - view_w/2;
  var norm_x = (window_x)/(view_w/2);
  
  var fovy = 2 * Math.atan(camera.m_zoom_factor * Math.tan(camera.m_fov*GEW_MATH_DEGREE_TO_RADIAN / 2));
  fovy /= GEW_MATH_DEGREE_TO_RADIAN;
  if (fovy < 0.0) 
    fovy = 180 + fovy;
  
  var near_height = camera.m_near * Math.tan(fovy*Math.PI/ 360.0);
  var y = near_height * norm_y;
  var x = near_height * camera.m_aspect_ratio * norm_x;
  
  gewRegVec0.set(x,y,-camera.m_near);
  invertViewMat.mulVec3NoTranslate(gewRegVec0,gewRegVec1);
  return new gewRay(camera.m_desired_pos,gewRegVec1);    
}
function transformWorldToCanvas(world_position,camera,view_proj_mat)
{
  var view_w = camera.m_viewport_size.x;
  var view_h = camera.m_viewport_size.y;
  
  var my_world_position = new gewVec4();
  my_world_position.set(world_position.x,world_position.y,world_position.z,1.0);
  var projected_point = view_proj_mat.mulVec4(my_world_position);
  var clip_point = gewRegVec0;
  clip_point.x = projected_point.x/projected_point.w;
  clip_point.y = projected_point.y/projected_point.w;
  clip_point.z = projected_point.z/projected_point.w;
  var canvas_point = new gewVec2();
  canvas_point.x = Math.round((clip_point.x+1)*view_w/2);
  canvas_point.y = Math.round((1-clip_point.y)*view_h/2);
  return canvas_point;  
}
function setShaderTexture(gl,gl_tex_name,shader_uniform,texture_unit_number)
{
  gl.uniform1i(shader_uniform, texture_unit_number);
  gl.activeTexture(gl.TEXTURE0+texture_unit_number);
  gl.bindTexture(gl.TEXTURE_2D, gl_tex_name);
}
